// Copyright Epic Games, Inc. All Rights Reserved.

#include "LyraSystemStatics.h"
#include "Engine/AssetManagerTypes.h"
#include "Engine/Engine.h"
#include "Engine/World.h"
#include "Engine/AssetManager.h"
#include "LyraLogChannels.h"
#include "Components/MeshComponent.h"
#include "GameModes/LyraUserFacingExperienceDefinition.h"

// 包含内联生成的 C++ 代码
#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraSystemStatics)

/**
 * 从主资产 ID 获取类型化的软对象引用
 * @param PrimaryAssetId 主资产 ID
 * @param ExpectedAssetType 期望的资产类型
 * @return 类型化的软对象引用
 */
TSoftObjectPtr<UObject> ULyraSystemStatics::GetTypedSoftObjectReferenceFromPrimaryAssetId(FPrimaryAssetId PrimaryAssetId, TSubclassOf<UObject> ExpectedAssetType)
{
	// 获取资产管理器实例
	if (UAssetManager* Manager = UAssetManager::GetIfInitialized())
	{
		// 主资产类型信息
		FPrimaryAssetTypeInfo Info;
		// 获取主资产类型信息
		if (Manager->GetPrimaryAssetTypeInfo(PrimaryAssetId.PrimaryAssetType, Info) && !Info.bHasBlueprintClasses)
		{
			// 获取资产基类
			if (UClass* AssetClass = Info.AssetBaseClassLoaded)
			{
				// 检查资产类是否继承自期望的资产类型
				if ((ExpectedAssetType == nullptr) || !AssetClass->IsChildOf(ExpectedAssetType))
				{
					return nullptr;
				}
			}
			else
			{
				// 记录警告，AssetBaseClassLoaded 未设置，无法验证，返回 null
				UE_LOG(LogLyra, Warning, TEXT("GetTypedSoftObjectReferenceFromPrimaryAssetId(%s, %s) - AssetBaseClassLoaded was unset so we couldn't validate it, returning null"),
					*PrimaryAssetId.ToString(),
					*GetPathNameSafe(*ExpectedAssetType));
			}

			// 返回主资产路径的软对象引用
			return TSoftObjectPtr<UObject>(Manager->GetPrimaryAssetPath(PrimaryAssetId));
		}
	}
	return nullptr;
}

/**
 * 从用户可见体验名称获取主资产 ID
 * @param AdvertisedExperienceID 用户可见体验 ID
 * @return 主资产 ID
 */
FPrimaryAssetId ULyraSystemStatics::GetPrimaryAssetIdFromUserFacingExperienceName(const FString& AdvertisedExperienceID)
{
	// 创建主资产类型
	const FPrimaryAssetType Type(ULyraUserFacingExperienceDefinition::StaticClass()->GetFName());
	// 返回主资产 ID
	return FPrimaryAssetId(Type, FName(*AdvertisedExperienceID));
}

/**
 * 开始下一局游戏
 * @param WorldContextObject 世界上下文对象
 */
void ULyraSystemStatics::PlayNextGame(const UObject* WorldContextObject)
{
	// 从世界上下文对象获取世界
	UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
	// 如果世界为空，返回
	if (World == nullptr)
	{
		return;
	}

	// 获取世界上下文
	const FWorldContext& WorldContext = GEngine->GetWorldContextFromWorldChecked(World);
	// 获取最后一个 URL
	FURL LastURL = WorldContext.LastURL;

// 在编辑器模式下，需要从地图名称中去除 PIE 前缀
#if WITH_EDITOR
	LastURL.Map = UWorld::StripPIEPrefixFromPackageName(LastURL.Map, WorldContext.World()->StreamingLevelsPrefix);
#endif

	// 添加无缝旅行选项，因为我们希望保持客户端连接。如果无缝旅行被禁用，将回退到硬旅行
	LastURL.AddOption(TEXT("SeamlessTravel"));

	// 获取 URL 字符串
	FString URL = LastURL.ToString();
	// 如果不移除主机/端口信息，服务器旅行将失败
	URL.RemoveFromStart(LastURL.GetHostPortString());
	
	// 是否绝对路径（我们想要使用 TRAVEL_Relative）
	const bool bAbsolute = false;
	// 是否应跳过游戏通知
	const bool bShouldSkipGameNotify = false;
	// 服务器旅行到新 URL
	World->ServerTravel(URL, bAbsolute, bShouldSkipGameNotify);
}

/**
 * 在目标角色上的所有网格组件的所有部分上设置标量参数值
 * @param TargetActor 目标角色
 * @param ParameterName 参数名称
 * @param ParameterValue 参数值
 * @param bIncludeChildActors 是否包含子角色
 */
void ULyraSystemStatics::SetScalarParameterValueOnAllMeshComponents(AActor* TargetActor, const FName ParameterName, const float ParameterValue, bool bIncludeChildActors)
{
	// 如果目标角色不为空
	if (TargetActor != nullptr)
	{
		// 遍历所有网格组件
		TargetActor->ForEachComponent<UMeshComponent>(bIncludeChildActors, [=](UMeshComponent* InComponent)
		{
			// 在材质上设置标量参数值
			InComponent->SetScalarParameterValueOnMaterials(ParameterName, ParameterValue);
		});
	}
}

/**
 * 在目标角色上的所有网格组件的所有部分上设置向量参数值
 * @param TargetActor 目标角色
 * @param ParameterName 参数名称
 * @param ParameterValue 参数值
 * @param bIncludeChildActors 是否包含子角色
 */
void ULyraSystemStatics::SetVectorParameterValueOnAllMeshComponents(AActor* TargetActor, const FName ParameterName, const FVector ParameterValue, bool bIncludeChildActors)
{
	// 如果目标角色不为空
	if (TargetActor != nullptr)
	{
		// 遍历所有网格组件
		TargetActor->ForEachComponent<UMeshComponent>(bIncludeChildActors, [=](UMeshComponent* InComponent)
		{
			// 在材质上设置向量参数值
			InComponent->SetVectorParameterValueOnMaterials(ParameterName, ParameterValue);
		});
	}
}

/**
 * 在目标角色上的所有网格组件的所有部分上设置颜色参数值
 * @param TargetActor 目标角色
 * @param ParameterName 参数名称
 * @param ParameterValue 参数值
 * @param bIncludeChildActors 是否包含子角色
 */
void ULyraSystemStatics::SetColorParameterValueOnAllMeshComponents(AActor* TargetActor, const FName ParameterName, const FLinearColor ParameterValue, bool bIncludeChildActors)
{
	// 调用设置向量参数值函数，将颜色转换为向量
	SetVectorParameterValueOnAllMeshComponents(TargetActor, ParameterName, FVector(ParameterValue), bIncludeChildActors);
}

/**
 * 查找继承自给定类的所有组件
 * @param TargetActor 目标角色
 * @param ComponentClass 组件类
 * @param bIncludeChildActors 是否包含子角色
 * @return 组件数组
 */
TArray<UActorComponent*> ULyraSystemStatics::FindComponentsByClass(AActor* TargetActor, TSubclassOf<UActorComponent> ComponentClass, bool bIncludeChildActors)
{
	// 组件数组
	TArray<UActorComponent*> Components;
	// 如果目标角色不为空
	if (TargetActor != nullptr)
	{
		// 获取组件
		TargetActor->GetComponents(ComponentClass, /*out*/ Components, bIncludeChildActors);

	}
	// 移动返回组件数组
	return MoveTemp(Components);
}